﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace IR
{

 
    class Post
    {
        int m_Doc_number;
        int m_Freq;
        double m_Tf_wt;
      
        double m_Wt;
        double m_Nomalized;


        public double Nomalized
        {
            get { return m_Nomalized; }
            set { m_Nomalized = value; }
        }
        public double Tf_wt
        {
            get { return m_Tf_wt; }
            set { m_Tf_wt = value; }
        }
        public double Wt
        {
            get { return m_Wt; }
            set { m_Wt = value; }
        }
        public int Doc_number
        {
            get { return m_Doc_number; }
        }
        public int Freq
        {
            get { return m_Freq; }
            set { m_Freq = value;  }
        }

        public Post(int doc_number,int freq)
        {
            m_Doc_number = doc_number;
            m_Freq = freq;
            m_Tf_wt = 1 + Math.Log10(m_Freq);
        }
    }
   
    class MyDictionary : IComparable<MyDictionary>
    {
        protected String m_Term;
        protected int m_Doc_number;

        protected int m_Term_Freq;

        public String Term
        {
            get { return m_Term; }

        }
        public int Doc_number
        {
            get { return m_Doc_number; }
        }
        public int Term_Freq
        {
            get { return m_Term_Freq; }
            set { m_Term_Freq = value; }
        }
  
        public MyDictionary(String term, int doc_number)
        {
            m_Term = term;
            m_Doc_number = doc_number;
            m_Term_Freq = 1;

            
        }
        public int CompareTo(MyDictionary other)
        {
            if (m_Term.CompareTo(other.m_Term) == 0)
            {
                return m_Doc_number.CompareTo(other.m_Doc_number);
            }

            return m_Term.CompareTo(other.m_Term);
        }
        

    }
    class IR_Dictionary
    {

        List<Post> m_Postings;
        int m_Call_freq;
        int m_N_Docs; // == df
        double m_idf;
       
        String m_term;
       

        public double Idf
        {
            get { return m_idf; }
            set { m_idf = value; }
        }
       
        public int Call_freq
        {
            get { return m_Call_freq; }
            set { m_Call_freq = value; }
        } 
        public String Term
        {
            get { return m_term; }
            set { m_term = value; }
        }
        public int N_Docs
        {
            get { return m_N_Docs; }
            set { m_N_Docs = value; }
        }

        public List<Post> Postings
        {
            get { return m_Postings; }
            
        }
        public IR_Dictionary(String term)
        {
            m_term = term;
            m_Call_freq = 0;
            m_N_Docs = 0;
            m_Postings = new List<Post>();
        }

    }


    class Index
    {
        List<MyDictionary> m_Dct;
        List<IR_Dictionary> m_IR_Dct;
       
        DirectoryInfo m_Dinfo_Out;
        TermParser m_Parser;
        string currentDrct;
        int m_num_of_total_docs;



        public int Num_of_total_docs
        {
            get { return m_num_of_total_docs; }
            set { m_num_of_total_docs = value; }
        }

        public List<IR_Dictionary> IR_Dct
        {
            get { return m_IR_Dct; }
            set { m_IR_Dct = value; }
        }
        public List<MyDictionary> Dct
        {
            get { return m_Dct; }
        }

        public Index()
        {
            currentDrct = Environment.CurrentDirectory;
            m_Dct = new List<MyDictionary>();
            m_Dinfo_Out = new DirectoryInfo(currentDrct + @"\output");
            m_Parser = new TermParser(@"(.*)/N");

        }
        public void AddDiction(string term, int n_doc)
        {
            m_Dct.Add( new MyDictionary(term , n_doc) );

        }

        private void step1()
        {
            StreamReader fReader;
            MatchCollection matches;
            char[] chr = { '.', 'o', 'u', 't' };

            //파일 하나씩 한번에 한라인씩 읽으며  파싱해서 사전에 추가 

            foreach (FileInfo f in m_Dinfo_Out.GetFiles())
            {
                fReader = new StreamReader(f.OpenRead(), Encoding.Default);//스트림 열고 
                int docNumber = Convert.ToInt32((f.Name.Trim(chr)));

                while (fReader.EndOfStream == false)
                {
                    //문서 번호 

                    string temp = fReader.ReadLine();

                    //한줄 읽어서 파싱 해서 매칭된 결과들 가져오고 
                    matches = m_Parser.getTerms(temp);

                    //매칭된게 1개 이상이면
                    if (matches != null && matches.Count != 0)
                    {

                        foreach (Match match in matches)
                        {
                            //index에 매칭된 텀이랑  문서 번호 넣기 
                            string term = match.ToString();
                            term = term.Trim('\t');
                            term = term.Replace("/N", "");
                            term = term.TrimStart().TrimEnd();

                            AddDiction(term, docNumber);
                        }

                    }

                }
                fReader.Close();

            }//  foreach (FileInfo f in m_Dinfo_Out.GetFiles())


            FileStream fff = new FileStream("debug_step1.txt", FileMode.Create);
            StreamWriter sw = new StreamWriter(fff);


            foreach (MyDictionary d in m_Dct)
            {
                sw.WriteLine("" + d.Term + " " + d.Doc_number);

            }
            sw.Close();
        }
        private void step2()
        {
            m_Dct.Sort();
            
            FileStream fff = new FileStream("debug_step2.txt", FileMode.Create);
            StreamWriter sw = new StreamWriter(fff);


            foreach (MyDictionary d in m_Dct)
            {
                sw.WriteLine("" + d.Term + " " + d.Doc_number);

            }
            sw.Close();
        }
        private void step3()
        {

            for (int i = 0; i < m_Dct.Count - 1; )
            {

                //같은 문서의 같은 term 일경우 
                if (m_Dct[i].Term == m_Dct[i + 1].Term &&
                    m_Dct[i].Doc_number == m_Dct[i + 1].Doc_number)
                {
                    m_Dct[i].Term_Freq++;
                    m_Dct.RemoveAt(i + 1);
                }
                else
                {
                    i++;
                }

            }

            FileStream fff = new FileStream("debug_step3.txt", FileMode.Create);
            StreamWriter sw = new StreamWriter(fff);

            foreach (MyDictionary m in m_Dct)
            {
                sw.WriteLine("" + m.Term + " " + m.Doc_number + " " + m.Term_Freq);
            }
            sw.Close();
        }

        private void Calc_idf_N_Wt(int i)
        {
            m_IR_Dct[i].Idf = Math.Log10(( (double)m_num_of_total_docs / (double)m_IR_Dct[i].N_Docs));

            foreach (Post p in m_IR_Dct[i].Postings)
            {
                p.Wt = p.Tf_wt * m_IR_Dct[i].Idf;

            }

        }

        private void step4()
        {
            m_IR_Dct = new List<IR_Dictionary>();
             
            foreach (MyDictionary m in m_Dct)
            {
                if (m_IR_Dct.Count == 0)
                {
                    m_IR_Dct.Add(new IR_Dictionary(m.Term)) ;
                    m_IR_Dct[m_IR_Dct.Count-1  ].Postings.Add(new Post(m.Doc_number, m.Term_Freq));
                    
                }
                else
                {
                    if (m_IR_Dct[m_IR_Dct.Count - 1].Term == m.Term)
                    {
                        m_IR_Dct[m_IR_Dct.Count - 1].Postings.Add(new Post(m.Doc_number, m.Term_Freq));
                        
                    }
                    else
                    {
                        //calc lasted idf 
                        Calc_idf_N_Wt(m_IR_Dct.Count - 1);
                        
                        
                        //add new term
                        m_IR_Dct.Add(new IR_Dictionary(m.Term));
                        m_IR_Dct[m_IR_Dct.Count - 1].Postings.Add(new Post(m.Doc_number, m.Term_Freq));
                        
                    }
 
                }
                m_IR_Dct[m_IR_Dct.Count - 1].N_Docs += 1;
                m_IR_Dct[m_IR_Dct.Count - 1].Call_freq += m.Term_Freq;
           
            }
          
            //마지막꺼 
            //calc lasted idf 
            Calc_idf_N_Wt(m_IR_Dct.Count - 1);
  
            //lines for debug
            FileStream fff = new FileStream("debug_step4.txt", FileMode.Create);
            StreamWriter sw = new StreamWriter(fff);

            foreach (IR_Dictionary i in m_IR_Dct)
            {
                sw.WriteLine("" + i.Term + " " + i.N_Docs + " " + i.Call_freq);
                foreach (Post p in i.Postings)
                {
                    sw.WriteLine("\t" + p.Doc_number + " " + p.Freq);
                }

            }
            sw.Close();
            
        
        
        }
       
        
        public void MakeIndex()
        {
            step1();
            step2();
            step3();
            step4();
            
        }

    }
}
